Stage 0

Rui Chen’s human fetal dataset was out in August: https://www.nature.com/articles/s41467-024-50853-5

Downloading the RNAseq fastq files (SRP510712) to biowulf2:/data/OGVFB_BG/scEiaD/fastq/ (2025-01-25)

Build a new scVI model directly off of their h5ad object (from broad)

  • 88444d73-7f55-4a62-bcfe-e929878c6c78.h5ad

Found a mouse dev study: https://www.nature.com/articles/s41598-023-28429-y

..and another human dev from Lako: https://www.nature.com/articles/s41467-024-47933-x?fromPaywallRec=false

Stage 1

  • pull data from one species
  • filter to age group (dev or mature)
  • select random (up to 2k) cell type per study and output those barcodes
  • output barcodes for the non-selected cells
  • run scvi on biowulf
library(tidyverse)
sample_meta <- data.table::fread('~/git/scEiaD_quant/sample_meta.scEiaD_v1.2025_02_03.02.tsv.gz')
cell_meta <- data.table::fread('~/data/scEiaD_modeling/hs111.adata.solo.20250204.obs.csv.gz')[,-1] %>% 
  relocate(barcode) %>% 
  filter(solo_doublet == "FALSE")

hs111_dev_eye <- cell_meta %>% 
  filter(study_accession != 'SRP362101') %>% 
  mutate(stage = case_when(as.numeric(age) <= 10 ~ 'Developing', 
                           TRUE ~ 'Mature'), 
         side = case_when(tissue == 'Brain Choroid Plexus' ~ 'Brain Choroid Plexus',
                          grepl("Choroid|RPE", tissue) ~ 'eye',
                          grepl("Retina", tissue) ~ 'eye',
                          grepl("Outf", tissue) ~ 'FrontEye',
                          grepl("Iris", tissue) ~ 'FrontEye',
                          grepl("Sclera", tissue) ~ 'FrontEye',
                          grepl("Cornea", tissue) ~ 'FrontEye',
                          grepl("Macula", tissue) ~ 'eye',
                          grepl("Trabecul", tissue) ~ 'FrontEye',
                          grepl("Optic", tissue) ~ 'eye',
                          TRUE ~ tissue)) %>% 
  filter(organ == 'Eye', # 2024 09 03 oops
         organism == 'Homo sapiens',
         !grepl("^#", sample_accession),
         source == 'Tissue',
         #tissue %in% c("Macula", "Retina"),
         #side %in% c("FrontEye", "eye"),
         #side %in% c("eye"),# 2024 08 31
         #capture_type == 'cell', # 2024 08 28
         #kb_tech %in% c("10xv1","10xv2","10xv3"), # 2024 08 28
         stage == 'Developing')# %>% 
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `stage = case_when(as.numeric(age) <= 10 ~ "Developing", TRUE ~ "Mature")`.
Caused by warning:
! NAs introduced by coercion
set.seed(2025-02-04)
#hs111_dev_eye$MajorCellType %>% table()
hs111_dev_ref_bcs <- hs111_dev_eye %>% 
  group_by(study_accession, MajorCellType) %>% 
  sample_n(2000, replace = TRUE) %>%  
  unique()

hs111_dev_query_bcs <- hs111_dev_eye %>% 
  filter(!barcode %in% hs111_dev_ref_bcs$barcode) 

#hs111_dev_ref_bcs$barcode %>% write(gzfile('~/git/scEiaD_modeling/data/hs111_dev_eye_ref_bcs.full.20250204.csv.gz'))
#hs111_dev_query_bcs$barcode %>% write(gzfile('~/git/scEiaD_modeling/data/hs111_dev_eye_query_bcs.full.20250204.csv.gz'))

run scVI

now go to biowulf2:/data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_mature_eye

cd /data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_developing_eye
source /data/$USER/conda/etc/profile.d/conda.sh && source /data/$USER/conda/etc/profile.d/mamba.sh
sbatch --time=8:00:00 snakecall.sh
/var/folders/s4/y5f1tt296dj8088gvczcx11d4lrnr7/T/Rtmp2zIYSv/chunk-code-e5041274c34.txt: line 1: cd: /data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_developing_eye: No such file or directory
/var/folders/s4/y5f1tt296dj8088gvczcx11d4lrnr7/T/Rtmp2zIYSv/chunk-code-e5041274c34.txt: line 2: /data/mcgaugheyd/conda/etc/profile.d/conda.sh: No such file or directory
/var/folders/s4/y5f1tt296dj8088gvczcx11d4lrnr7/T/Rtmp2zIYSv/chunk-code-e5041274c34.txt: line 3: sbatch: command not found

rsync output from biowulf2 to local computer

cd /Users/mcgaugheyd/data/scEiaD_modeling/hs111_developing_eye
rsync -Prav h2:/data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_developing_eye/*obs* .
                           ***WARNING***

You are accessing a U.S. Government information system, which includes
(1) this computer, (2) this computer network, (3) all computers 
connected to this network, and (4) all devices and storage media 
attached to this network or to a computer on this network. This 
information system is provided for U.S.  Government-authorized use only.

Unauthorized or improper use of this system may result in disciplinary
action, as well as civil and criminal penalties.

By using this information system, you understand and consent to the
following:

* You have no reasonable expectation of privacy regarding any 
communications or data transiting or stored on this information system. 
At any time, and for any lawful Government purpose, the government may 
monitor, intercept, record, and search and seize any communication or 
data transiting or stored on this information system.

* Any communication or data transiting or stored on this information 
system may be disclosed or used for any lawful Government purpose.

--
Notice to users:  This system is rebooted for patches and maintenance on
the first Sunday of every month at 8:00 pm unless Monday is a holiday, in
which case it is rebooted the following Sunday evening at 8:00 pm.  Running 
cluster jobs are not affected by the monthly reboot. 
receiving file list ... 
23 files to consider

sent 16 bytes  received 748 bytes  305.60 bytes/sec
total size is 1161560763  speedup is 1520367.49

run CT predictions from our collated models

cd /data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_developing_eye
source /data/$USER/conda/etc/profile.d/conda.sh && source /data/$USER/conda/etc/profile.d/mamba.sh
mamba activate rapids_singlecell
# hand cut down the all human h5ad as it was using too much memory in python
# import scanpy as sc
# big_adata = sc.read_h5ad('../../hs111.adata.solo.20250204.h5ad')
# dev_adata = sc.read_h5ad('hs111_dev_eye_20250204_2000hvg_200e_30l.h5ad)
# new_adata = big_adata[dev_adata.obs_names,:]
# new_adata.write_h5ad("snakeout/hs111_developing_eye/hs111.adata.solo.20250204.dev.h5ad")
python ~/git/scEiaD_modeling/workflow/scripts/ct_projection.py  hs111.adata.solo.20250204.dev.h5ad models_human.tsv ct_predictions__hs111.adata.solo.20250204.csv.gz
/var/folders/s4/y5f1tt296dj8088gvczcx11d4lrnr7/T/Rtmp2zIYSv/chunk-code-e5047a7f0844.txt: line 1: cd: /data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_developing_eye: No such file or directory
/var/folders/s4/y5f1tt296dj8088gvczcx11d4lrnr7/T/Rtmp2zIYSv/chunk-code-e5047a7f0844.txt: line 2: /data/mcgaugheyd/conda/etc/profile.d/conda.sh: No such file or directory
/var/folders/s4/y5f1tt296dj8088gvczcx11d4lrnr7/T/Rtmp2zIYSv/chunk-code-e5047a7f0844.txt: line 3: mamba: command not found
/var/folders/s4/y5f1tt296dj8088gvczcx11d4lrnr7/T/Rtmp2zIYSv/chunk-code-e5047a7f0844.txt: line 10: python: command not found

Stage 3

Assess Output

source('analysis_scripts.R')

obs <- pull_obs('~/data/scEiaD_modeling/hs111_developing_eye/hs111_dev_eye_20250204_2000hvg_200e_30l.obs.csv.gz', machine_label = 'scANVI_MCT')
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.
ct_predictions <- data.table::fread("~/data/scEiaD_modeling/hs111_developing_eye/ct_predictions__hs111.adata.solo.20250204.csv.gz") %>% select(-17)

obs$obs <- obs$obs %>% left_join(ct_predictions %>% select(barcode, CT__chen_fetal_hrca, umap1_chen_fetal_hrca,umap2_chen_fetal_hrca, CT__sceiad_20250107_full), by = c("barcodei" = 'barcode'))

UMAPs

obs$obs %>% 
  left_join(obs$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = scANVI_MCT), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(scANVI_MCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = scANVI_MCT, color = scANVI_MCT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("MajorCellType (scANVI)")


obs$obs %>%
  filter(MajorCellType != 'unlabelled') %>% 
  mutate(MajorCellType = case_when(SubCellType == 'NRPC' ~ 'neurogenic',
                                   TRUE ~ MajorCellType)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = MajorCellType), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(MajorCellType) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = MajorCellType, color = MajorCellType)) +
  scale_color_manual(values = c(pals::glasbey(),pals::alphabet2()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("MajorCellType")


obs$obs %>%
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT__chen_fetal_hrca), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(CT__chen_fetal_hrca) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = CT__chen_fetal_hrca, color = CT__chen_fetal_hrca)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("CT__chen_fetal_hrca")


obs$obs %>%
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT__sceiad_20250107_full), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(CT__sceiad_20250107_full) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = CT__sceiad_20250107_full, color = CT__sceiad_20250107_full)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("CT__sceiad_20250107_full")

By leiden3

obs$obs %>%
  left_join(obs$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = as.factor(leiden3)), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_text_repel(data = . %>% group_by(mCT, leiden3) %>%
                             summarise(umap1 = median(umap1),
                                       umap2 = median(umap2),),
                           aes(label = paste0(mCT,'-',leiden3)), bg.color = 'white') +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet(), pals::kelly()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("leiden3") 

Bipolar / rod precursor issue

This representation has a big “blob” (clusters 11, 31) which are an amalgamation of nrpc (neurogenic) / rod precursor / bipolar precursor cells. Pulling in another scVI representation (with fewer epochs and more latent dimensions which, empirically, tends to more clearly distinguish different cell types).

This representation better distinguishes nrpc (22), rod precursor (20), and bipolar precursor (45)

obs50 <- pull_obs('~/data/scEiaD_modeling/hs111_developing_eye/hs111_dev_eye_20250204_2000hvg_50e_50l.obs.csv.gz', machine_label = 'scANVI_MCT')
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.
obs50$obs %>% 
  left_join(obs50$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = scANVI_MCT), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(scANVI_MCT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = scANVI_MCT, color = scANVI_MCT)) +
  scale_color_manual(values = c(pals::glasbey(),pals::alphabet2()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("MajorCellType (scANVI)")


obs50$obs %>%
  left_join(obs50$labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = as.factor(leiden3)), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_text_repel(data = . %>% group_by(mCT, leiden3) %>%
                             summarise(umap1 = median(umap1),
                                       umap2 = median(umap2),),
                           aes(label = paste0(mCT,'-',leiden3)),bg.color = 'white') +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet(), pals::kelly()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("leiden3") 


obs50$obs %>%
  left_join(obs50$labels, by = 'leiden3') %>% 
  filter(leiden3 %in% c(22,20,45)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = as.factor(leiden3)), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(mCT, leiden3) %>%
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2),),
                            aes(label = paste0(mCT,'-',leiden3))) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet(), pals::kelly()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("leiden3") 


obs$obs %>% left_join(obs50$obs %>% select(barcodei, leiden3_50 = leiden3), by = 'barcodei') %>% group_by(leiden3, leiden3_50) %>% summarise(Count = n()) %>% mutate(Ratio = Count/sum(Count)) %>% filter(leiden3 %in% c(11,31), Ratio > 0.1)
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.

obs leiden3 <-> obs_50 leiden3

obs$obs %>% 
  left_join(obs50$obs %>% select(barcodei, leiden3_50 = leiden3), by = 'barcodei') %>% 
  group_by(leiden3, leiden3_50) %>% 
  summarise(Count = n()) %>% 
  mutate(Ratio = Count/sum(Count)) %>% 
  filter(Ratio > 0.1) %>%
  mutate(leiden3 = as.factor(leiden3),
         leiden3_50 = as.factor(leiden3_50)) %>% 
  DT::datatable(filter = 'top')
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.

Tables

obs$obs <- obs$obs %>% 
  mutate(CT__chen_fetal_hrca_core = case_when(grepl("AC\\d",CT__chen_fetal_hrca) ~ 'amacrine',
                                              CT__chen_fetal_hrca == 'MG' ~ 'mueller',
                                              CT__chen_fetal_hrca %in% c("BB_GB", "FMB", "IMB") ~ 'bipolar',
                                              grepl("DB\\d",CT__chen_fetal_hrca) ~ 'bipolar',
                                              grepl("OFF|ON", CT__chen_fetal_hrca) ~ 'retinal ganglion',
                                              CT__chen_fetal_hrca == 'S_Cone' ~ 'cone (s)',
                                              CT__chen_fetal_hrca == 'ML_Cone' ~ 'cone (ml)',
                                              CT__chen_fetal_hrca == 'RBC' ~ 'red blood',
                                              CT__chen_fetal_hrca == 'RGC Precursor' ~ 'retinal ganglion precursor',
                                              CT__chen_fetal_hrca == 'BC Precursor' ~ 'bipolar precursor',
                                              CT__chen_fetal_hrca == 'AC Precursor' ~ 'amacrine precursor',
                                              CT__chen_fetal_hrca == 'HC Precursor' ~ 'horizontal precursor',
                                              TRUE ~ tolower(CT__chen_fetal_hrca)))

obs$obs %>% 
  group_by(leiden3, CT__chen_fetal_hrca_core) %>% 
  summarise(Count = n(), Age = mean(age)) %>% 
  left_join(obs$obs %>% group_by(leiden3) %>% summarise(Total = n())) %>% 
  mutate(Ratio = Count / Total) %>% 
  filter(Ratio > 0.01) %>% arrange(leiden3, -Ratio) %>% 
  mutate(leiden3 = as.factor(leiden3)) %>% 
  DT::datatable(filter = 'top')
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.Joining with `by = join_by(leiden3)`
obs$obs %>% 
  group_by(leiden3, scANVI_MCT, CT__chen_fetal_hrca_core, CT__sceiad_20250107_full) %>% 
  summarise(Count = n(), Age = mean(age)) %>% 
  left_join(obs$obs %>% group_by(leiden3) %>% summarise(Total = n())) %>% 
  mutate(Ratio = Count / Total) %>% 
  filter(Ratio > 0.01) %>% arrange(leiden3, -Ratio) %>% 
  mutate(leiden3 = as.factor(leiden3)) %>% 
  DT::datatable(filter = 'top')
`summarise()` has grouped output by 'leiden3', 'scANVI_MCT', 'CT__chen_fetal_hrca_core'. You can override using the `.groups` argument.Joining with `by = join_by(leiden3)`

Hand Label Changes

First take the chen cell labels, then hand alter anything that needs fixing

labels <- obs$obs %>% 
  mutate(CT__chen_fetal_hrca_core = gsub("precursor","(precursor)", CT__chen_fetal_hrca_core)) %>% 
  group_by(leiden3, CT__chen_fetal_hrca_core) %>% 
  summarise(Count = n()) %>% 
  slice_max(order_by = Count, n = 1) %>% 
  mutate(CT__chen_fetal_hrca_core = case_when(CT__chen_fetal_hrca_core == 'nrpc' ~ 'neurogenic',
                                              TRUE ~ CT__chen_fetal_hrca_core))
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.
label_change <- rbind(
  c(9, 'fibroblast'),
  c(11, 'bipolar (precursor)'),
  c(18, 'amacrine (precursor)'),
  c(19, 'fibroblast'),
  c(31, 'bipolar (precursor)'),
  c(32, 'retinal ganglion (precursor)'),
  c(33, 'fibroblast'),
  c(36, 'amacrine (precursor)'),
  c(35, 'horizontal'),
  c(40, 'rod (precursor)'),
  c(51, 'rod (precursor)'),
  c(57, 'fibroblast'),
  c(50, 'horizontal'),
  c(58, 'keratocyte'),
  c(62, 'fibroblast'),
  c(65, 'horizontal'),
  c(66, 'fibroblast'),
  c(68, 'astrocyte'),
  c(70, 'microglia'),
  c(74, 'keratocyte'),
  c(75, 'rod'),
  c(76, 'endothelial'),
  c(78, 'red blood'),
  c(81, 'rpe'),
  c(82, 'fibroblast'),
  c(86, 'red blood'),
  c(87, 'muscle (ciliary)'),
  c(88, 'bipolar (precursor)'),
  c(89, 'neurogenic'),
  c(90, 'epithelial'),
  c(92, 'bipolar')) %>% as_tibble() %>% 
  mutate(V1 = as.integer(V1)) %>% 
  dplyr::rename(CT = V2, leiden3 = V1)

labels <- labels %>% left_join(label_change) %>% 
  mutate(CT = case_when(is.na(CT) ~ CT__chen_fetal_hrca_core,
                        TRUE ~ CT))
Joining with `by = join_by(leiden3)`
obs$obs %>% 
  left_join(labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(CT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = CT, color = CT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")




obs$obs %>% 
  left_join(obs$labels, by = 'leiden3') %>% 
  left_join(labels, by = 'leiden3') %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = as.factor(leiden3)), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_text_repel(data = . %>% group_by(mCT, leiden3) %>% 
                             summarise(umap1 = median(umap1),
                                       umap2 = median(umap2)),
                           aes(label = paste0(mCT, ' - ', leiden3)), bg.color = 'white') +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet(), pals::kelly()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")

NA
NA

hclust

Take pseudobulk values (at the cluster level) and hierarchically cluster them to ensure there aren’t any issues in either the overall structure (e.g. rod and cones are intersperse)d and/or to identify any potential mislabeled clusters

pb <- data.table::fread('~/data/scEiaD_modeling/hs111_developing_eye/hs111_dev_eye_20250204_2000hvg_200e_30l.pseudoBulk.leiden3.csv.gz')
colnames(pb) <- gsub("\\.\\d+","",colnames(pb))
hvg <- data.table::fread('~/data/scEiaD_modeling/hs111_developing_eye/hvg2000.csv.gz')[-1,]
rnames <- pb$V1
clust <- str_extract(rnames, '\\d+') %>% as.integer()
pb <- pb[,-1] %>% as.matrix()
row.names(pb) <- as.character(clust)
pb <- pb[as.character(obs$labels$leiden3),]

pb_norm <- metamoRph::normalize_data(t(pb), sample_scale = 'cpm') %>% t() 
Sample CPM scaling
log1p scaling
pb_norm <- pb_norm[,hvg$V2]
#pb_norm <- pb_norm[,hvg$V2[!hvg$V2 %in% c(cc_genes,ribo_genes)]]
# https://stats.stackexchange.com/questions/31565/compute-a-cosine-dissimilarity-matrix-in-r
sim <- pb_norm / sqrt(rowSums(pb_norm * pb_norm))
sim <- sim %*% t(sim)
D_sim <- as.dist(1 - sim)

hclust_sim <- hclust(D_sim, method = 'average')

hclust_sim$labels <- obs$labels %>% pull(leiden3)

library(ggtree)
p <- ggtree(hclust_sim)
p$data <- p$data %>% left_join(labels, by = c("label" = "leiden3"))
p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, CT, sep = ' - '), color = CT)) + 
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")




p <- ggtree(hclust_sim)
p$data <- p$data %>% 
  left_join(labels, by = c("label" = "leiden3")) %>% 
  left_join(obs$labels %>% mutate(studies = case_when(studyCount ==1 ~ studies,
                                                      TRUE ~ "multiple")), by = c("label" = "leiden3")) 

p + layout_dendrogram() +
  geom_tiplab(aes(label = paste(label, CT, studies, sep = ' - '), color = CT)) + 
  geom_tippoint(aes(shape = studies), size= 3) +
  theme_dendrogram(plot.margin=margin(16,16,300,16)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  guides(color="none")

NA
NA
NA

Remove Clusters

Combination of these reasons: - non-neural with neural - low n clusters CT far apart from same CT - study specific - umap looks “strange”

remove_leiden3 <- c(32, 42, 66, 82,88, 91, 92, 98, 99,
                    40, 51 ) # combo of keratin/rho expression...

CT by CT

diff <- pull_diff("~/data/scEiaD_modeling/hs111_developing_eye/hs111_dev_eye_20250204_2000hvg_200e_30l.difftesting.leiden3.csv.gz")
'select()' returned 1:many mapping between keys and columns
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
conv_table <- AnnotationDbi::select(org.Hs.eg.db::org.Hs.eg.db,
                                    keys=gsub('\\.\\d+','',unique(diff$diff_testing$ENSEMBL)),
                                    columns=c("ENSEMBL","SYMBOL", "MAP","GENENAME", "ENTREZID"), keytype="ENSEMBL")
'select()' returned 1:many mapping between keys and columns
library(ComplexHeatmap)

hm_maker <- function(markers, target, 
                     cdiff = diff, 
                     clabels = labels, 
                     remove = remove_leiden3){
  tib <- cdiff$diff_testing %>% 
    left_join(clabels, by = c('base'='leiden3')) %>% 
    left_join(conv_table %>% select(SYMBOL, ENSEMBL) %>% unique()) %>% 
    filter(SYMBOL %in% markers) %>% 
    mutate(base = as.character(base),
           base = paste0(base, ' - ', CT)) %>% 
    select(SYMBOL, base, logfoldchanges) %>% 
    pivot_wider(values_from = logfoldchanges, names_from = base)
  
  mat <- tib %>% select(-1) %>% as.matrix()
  row.names(mat) <- tib %>% pull(1)
  
  ha_column = ComplexHeatmap::HeatmapAnnotation(df = data.frame(Target = ifelse(grepl(target, colnames(tib)[-1]), "Target","Not"),
                                                                Remove = ifelse(str_extract(colnames(tib)[-1], '\\d+') %in% remove, "Remove","Retain")),        
                                                col = list(Target = c("Target" = "black","Not" = "white"),
                                                           Remove = c("Remove" = "red", "Retain" = "white")))
  
  col_fun = circlize::colorRamp2(c(-6, 0, 6), c("blue", "white", "red"))
  draw(Heatmap(mat, col=col_fun,
               name = 'logFoldChange',
               top_annotation = ha_column)
  )
}
markers <- c('PRKCA','GRM6','GRIK1')
target <- "bipolar"
hm_maker(markers, target)
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

RPC / Neurogenic

# markers <- c("HES1",
#              "ZFP36L2",
#              # "HES6",
#              # "ATOH7",
#              "VIM",
#              "CCND1",
#              "SFRP2",
#              "SPP1",
#              "ZFP36L1",
#              "TF",
#              "FOS",
#              "TTYH1")
mellough_markers <- read_csv("~/git/eyeMarkers/lists/rpc_markers__Mellough2019.csv")
Rows: 75 Columns: 2── Column specification ───────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): HGNC, Cell Type
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
markers <- mellough_markers %>% filter(`Cell Type` == 'RPC') %>% pull(HGNC)
hm_maker(c(markers, "PAX6","NEUROD1","ATOH7","HES6"), "rpc|neuro")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Bipolar

markers <- c("GRIK1","IRX6","LRTM1","PCP2","PRKCA","TRPM1","VSX1","VSX2")
#markers <- mellough_markers %>% filter(`Cell Type` == 'Bipolar') %>% pull(HGNC)
hm_maker(markers, "bipolar")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Fibroblast / Endo / Epi / Keratocyte

hm_maker(markers, "kera")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

RPE


markers <- c("PMEL","TYRP1","RPE65","BEST1","DCT")

hm_maker(markers, "rpe")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

astrocyte


markers <-   c("GFAP", 'PAX2')
#markers <- mellough_markers %>% filter(`Cell Type` == 'Astrocytes') %>% pull(HGNC)
hm_maker(markers, "astrocyte")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Horizontal


markers <- c("LHX1","ISL1","ONECUT1")

hm_maker(markers, "hori")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Amacrine


markers <- c('GAD1','GAD2','SLC6A9','NFIA')
markers <- mellough_markers %>% filter(`Cell Type` == 'Amacrine') %>% pull(HGNC)
hm_maker(markers, "amacr")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Ganglion


markers <-  mellough_markers %>% filter(`Cell Type` == 'RGC') %>% pull(HGNC)
hm_maker(markers, "ganglion")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Photoreceptors


markers <-  c('ARR3','OPN1LW','OPN1SW','RHO', 'OPN1MW', 'RCVRN',"CRX","PROM1","CNGA1","PDE6A")
#markers <-  mellough_markers %>% filter(`Cell Type` %in% c('Rod','Cone')) %>% pull(HGNC)
hm_maker(markers, "rod|cone")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Immune


markers <-  c("LYVE1","CD163",
              "C1QA","CTSS","B2M","HLA-DPA1","HLA-DPB1", "HLA-DRA",
              "CD27","CD79A",
              "CD2",
              "IL1RL1",
              "HBB","HBA")

hm_maker(markers, "microglia|blood")
Joining with `by = join_by(ENSEMBL)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

Updated UMAP

Reminder: nrpc (22), rod precursor (20), and bipolar precursor (45) from “obs50”

obs$obs %>% 
  left_join(labels, by = 'leiden3') %>% 
  mutate(CT = case_when(barcodei %in% (obs50$obs %>% filter(leiden3 == 22) %>% 
                                         pull(barcodei)) ~ 'nrpc',
                        barcodei %in% (obs50$obs %>% filter(leiden3 == 20) %>% 
                                         pull(barcodei)) ~ 'rod (precursor)',
                        barcodei %in% (obs50$obs %>% filter(leiden3 == 45) %>% 
                                         pull(barcodei)) ~ 'bipolar (precursor)',
                        TRUE ~ CT)) %>% 
  filter(!leiden3 %in% remove_leiden3) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(CT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = CT, color = CT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none")


obs$obs %>% 
  left_join(labels, by = 'leiden3') %>% 
  filter(!leiden3 %in% remove_leiden3) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = CT), pointsize = 1.1, alpha = 0.5) +
  # ggrepel::geom_label_repel(data = . %>% group_by(CT) %>% 
  #                             summarise(umap1 = median(umap1),
  #                                       umap2 = median(umap2)),
  #                           aes(label = CT, color = CT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") + facet_wrap(~CT)

Stage 4

Remake the scVI models with the updated CT calls (and cell removal)

Also fix the srp510712 NRPC getting labelled as RPC instead of neurogenic

nobs <- obs$obs %>% 
  left_join(labels, by = 'leiden3') %>% 
  filter(!leiden3 %in% remove_leiden3) %>% 
  mutate(CT = case_when(barcodei %in% (obs50$obs %>% filter(leiden3 == 22) %>% 
                                         pull(barcodei)) ~ 'neurogenic',
                        barcodei %in% (obs50$obs %>% filter(leiden3 == 20) %>% 
                                         pull(barcodei)) ~ 'rod (precursor)',
                        barcodei %in% (obs50$obs %>% filter(leiden3 == 45) %>% 
                                         pull(barcodei)) ~ 'bipolar (precursor)',
                        TRUE ~ CT)) %>% 
  mutate(MajorCellType = case_when(SubCellType == 'NRPC' ~ 'neurogenic',
                                   TRUE ~ MajorCellType))

set.seed(2025-02-10)
ref <- nobs %>% group_by(study_accession, CT) %>% 
  slice_sample(n = 2000, replace = TRUE) %>% 
  unique()

query <- nobs %>% filter(!barcodei %in% ref$barcodei)

ref$barcodei %>% write(gzfile('~/git/scEiaD_modeling/data/hs111_dev_eye_ref_bcs.full.20250211.stage4.csv.gz'))
query$barcodei %>% write(gzfile('~/git/scEiaD_modeling/data/hs111_dev_eye_query_bcs.full.20250211.stage4.csv.gz'))

nobs %>% dplyr::rename(barcode = barcodei) %>% write_csv('~/git/scEiaD_modeling/data/Human_Developing_Eye__stage4_CTcalls.freeze20250211.csv.gz')

Apply new CT calls to a new h5ad

cd /data/OGVFB_BG/scEiaD/2024_02_28/snakeout/hs111_developing_eye/stage4
source /data/$USER/conda/etc/profile.d/conda.sh && source /data/$USER/conda/etc/profile.d/mamba.sh
mamba activate rapids_singlecell

python ~/git/scEiaD_modeling/workflow/scripts/append_obs.py ../hs111.adata.solo.20250204.dev.h5ad /home/mcgaugheyd/git/scEiaD_modeling/data/Human_Developing_Eye__stage4_CTcalls.freeze20250211.csv.gz  hs111.adata.solo.20250211.dev.stage4CT.h5ad --transfer_columns MajorCellType,CT

# run scVI snake pipeline again
sbatch --time=8:00:00 snakecall.sh
obs_stage4 <- pull_obs('~/data/scEiaD_modeling/hs111_developing_eye/stage4/hs111_dev_eye_stage4_20250211_2000hvg_200e_50l.obs.csv.gz', machine_label = 'scanvi_CT', label = 'CT')
`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.`summarise()` has grouped output by 'leiden3'. You can override using the `.groups` argument.

obs_stage4$obs %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  
  scattermore::geom_scattermore(aes(color = scanvi_CT), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_label_repel(data = . %>% group_by(scanvi_CT) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = scanvi_CT, color = scanvi_CT)) +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey()) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("scanvi_CT")


obs_stage4$obs %>% 
  left_join(obs_stage4$labels, by = 'leiden3') %>% 
  #filter(scanvi_CT == 'rod (precursor)') %>% 
  #filter(leiden3 %in% c(2,6)) %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = as.factor(leiden3)), pointsize = 0.8, alpha = 0.5) +
  ggrepel::geom_text_repel(data = . %>% group_by(mCT, leiden3) %>% 
                              summarise(umap1 = median(umap1),
                                        umap2 = median(umap2)),
                            aes(label = paste0(mCT, ':', leiden3)), 
                            color = 'black', bg.color = 'white') +
  scale_color_manual(values = c(pals::alphabet2(), pals::glasbey(), pals::alphabet(), pals::kelly(), pals::brewer.set1(10)) %>% unname()) + 
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("leiden3 - scanvi_CT")


obs_stage4$obs %>% 
  ggplot(aes(x=umap1,y=umap2)) +
  scattermore::geom_scattermore(aes(color = scanvi_CT), pointsize = 0.8, alpha = 0.5) +
  cowplot::theme_cowplot() + theme(legend.position = "none") +
  ggtitle("scanvi_CT") +
  facet_wrap(~scanvi_CT) +
  ggtitle("scanvi_CT")

NA
NA
sessionInfo()
R version 4.4.1 (2024-06-14)
Platform: aarch64-apple-darwin20
Running under: macOS Sonoma 14.7.4

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: America/New_York
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] compiler_4.4.1    fastmap_1.2.0     cli_3.6.3         htmltools_0.5.8.1 tools_4.4.1      
 [6] rstudioapi_0.16.0 yaml_2.3.10       rmarkdown_2.27    knitr_1.48        xfun_0.48        
[11] digest_0.6.36     rlang_1.1.4       evaluate_0.24.0  
LS0tCnRpdGxlOiAiSHVtYW4gRGV2ZWxvcG1lbnQgRXllIENyZWF0aW9uIgpvdXRwdXQ6CiBodG1sX25vdGVib29rOgogIGF1dGhvcjogIkRhdmlkIE1jR2F1Z2hleSIKICBkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCiAgdGhlbWU6IGZsYXRseQogIHRvYzogdHJ1ZQogIHRvY19mbG9hdDogdHJ1ZQogIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBtZXNzYWdlID0gRkFMU0UsICB3YXJuaW5nID0gRkFMU0UsCiAgY29sbGFwc2UgPSBUUlVFLAogIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOCwKICBjb21tZW50ID0gIiM+IiwKICBkcGk9MzAwCikKYGBgCgojIFN0YWdlIDAKClJ1aSBDaGVuJ3MgaHVtYW4gZmV0YWwgZGF0YXNldCB3YXMgb3V0IGluIEF1Z3VzdDoKaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE0NjctMDI0LTUwODUzLTUKCkRvd25sb2FkaW5nIHRoZSBSTkFzZXEgZmFzdHEgZmlsZXMgKFNSUDUxMDcxMikgdG8gYmlvd3VsZjI6L2RhdGEvT0dWRkJfQkcvc2NFaWFEL2Zhc3RxLyAoMjAyNS0wMS0yNSkKCkJ1aWxkIGEgbmV3IHNjVkkgbW9kZWwgZGlyZWN0bHkgb2ZmIG9mIHRoZWlyIGg1YWQgb2JqZWN0IChmcm9tIGJyb2FkKQoKLSA4ODQ0NGQ3My03ZjU1LTRhNjItYmNmZS1lOTI5ODc4YzZjNzguaDVhZAoKCkZvdW5kIGEgbW91c2UgZGV2IHN0dWR5OiBodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU5OC0wMjMtMjg0MjkteQoKLi5hbmQgYW5vdGhlciBodW1hbiBkZXYgZnJvbSBMYWtvOiBodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTQ2Ny0wMjQtNDc5MzMteD9mcm9tUGF5d2FsbFJlYz1mYWxzZQoKIyBTdGFnZSAxCgotIHB1bGwgZGF0YSBmcm9tIG9uZSBzcGVjaWVzCi0gZmlsdGVyIHRvIGFnZSBncm91cCAoZGV2IG9yIG1hdHVyZSkKLSBzZWxlY3QgcmFuZG9tICh1cCB0byAyaykgY2VsbCB0eXBlIHBlciBzdHVkeSBhbmQgb3V0cHV0IHRob3NlIGJhcmNvZGVzCi0gb3V0cHV0IGJhcmNvZGVzIGZvciB0aGUgbm9uLXNlbGVjdGVkIGNlbGxzCi0gcnVuIHNjdmkgb24gYmlvd3VsZgoKCmBgYHtyLCBleGVjID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpzYW1wbGVfbWV0YSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgnfi9naXQvc2NFaWFEX3F1YW50L3NhbXBsZV9tZXRhLnNjRWlhRF92MS4yMDI1XzAyXzAzLjAyLnRzdi5neicpCmNlbGxfbWV0YSA8LSBkYXRhLnRhYmxlOjpmcmVhZCgnfi9kYXRhL3NjRWlhRF9tb2RlbGluZy9oczExMS5hZGF0YS5zb2xvLjIwMjUwMjA0Lm9icy5jc3YuZ3onKVssLTFdICU+JSAKICByZWxvY2F0ZShiYXJjb2RlKSAlPiUgCiAgZmlsdGVyKHNvbG9fZG91YmxldCA9PSAiRkFMU0UiKQoKaHMxMTFfZGV2X2V5ZSA8LSBjZWxsX21ldGEgJT4lIAogIGZpbHRlcihzdHVkeV9hY2Nlc3Npb24gIT0gJ1NSUDM2MjEwMScpICU+JSAKICBtdXRhdGUoc3RhZ2UgPSBjYXNlX3doZW4oYXMubnVtZXJpYyhhZ2UpIDw9IDEwIH4gJ0RldmVsb3BpbmcnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdNYXR1cmUnKSwgCiAgICAgICAgIHNpZGUgPSBjYXNlX3doZW4odGlzc3VlID09ICdCcmFpbiBDaG9yb2lkIFBsZXh1cycgfiAnQnJhaW4gQ2hvcm9pZCBQbGV4dXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJDaG9yb2lkfFJQRSIsIHRpc3N1ZSkgfiAnZXllJywKICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiUmV0aW5hIiwgdGlzc3VlKSB+ICdleWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJPdXRmIiwgdGlzc3VlKSB+ICdGcm9udEV5ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIklyaXMiLCB0aXNzdWUpIH4gJ0Zyb250RXllJywKICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiU2NsZXJhIiwgdGlzc3VlKSB+ICdGcm9udEV5ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIkNvcm5lYSIsIHRpc3N1ZSkgfiAnRnJvbnRFeWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJNYWN1bGEiLCB0aXNzdWUpIH4gJ2V5ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIlRyYWJlY3VsIiwgdGlzc3VlKSB+ICdGcm9udEV5ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoIk9wdGljIiwgdGlzc3VlKSB+ICdleWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB0aXNzdWUpKSAlPiUgCiAgZmlsdGVyKG9yZ2FuID09ICdFeWUnLCAjIDIwMjQgMDkgMDMgb29wcwogICAgICAgICBvcmdhbmlzbSA9PSAnSG9tbyBzYXBpZW5zJywKICAgICAgICAgIWdyZXBsKCJeIyIsIHNhbXBsZV9hY2Nlc3Npb24pLAogICAgICAgICBzb3VyY2UgPT0gJ1Rpc3N1ZScsCiAgICAgICAgICN0aXNzdWUgJWluJSBjKCJNYWN1bGEiLCAiUmV0aW5hIiksCiAgICAgICAgICNzaWRlICVpbiUgYygiRnJvbnRFeWUiLCAiZXllIiksCiAgICAgICAgICNzaWRlICVpbiUgYygiZXllIiksIyAyMDI0IDA4IDMxCiAgICAgICAgICNjYXB0dXJlX3R5cGUgPT0gJ2NlbGwnLCAjIDIwMjQgMDggMjgKICAgICAgICAgI2tiX3RlY2ggJWluJSBjKCIxMHh2MSIsIjEweHYyIiwiMTB4djMiKSwgIyAyMDI0IDA4IDI4CiAgICAgICAgIHN0YWdlID09ICdEZXZlbG9waW5nJykjICU+JSAKCnNldC5zZWVkKDIwMjUtMDItMDQpCiNoczExMV9kZXZfZXllJE1ham9yQ2VsbFR5cGUgJT4lIHRhYmxlKCkKaHMxMTFfZGV2X3JlZl9iY3MgPC0gaHMxMTFfZGV2X2V5ZSAlPiUgCiAgZ3JvdXBfYnkoc3R1ZHlfYWNjZXNzaW9uLCBNYWpvckNlbGxUeXBlKSAlPiUgCiAgc2FtcGxlX24oMjAwMCwgcmVwbGFjZSA9IFRSVUUpICU+JSAgCiAgdW5pcXVlKCkKCmhzMTExX2Rldl9xdWVyeV9iY3MgPC0gaHMxMTFfZGV2X2V5ZSAlPiUgCiAgZmlsdGVyKCFiYXJjb2RlICVpbiUgaHMxMTFfZGV2X3JlZl9iY3MkYmFyY29kZSkgCgojaHMxMTFfZGV2X3JlZl9iY3MkYmFyY29kZSAlPiUgd3JpdGUoZ3pmaWxlKCd+L2dpdC9zY0VpYURfbW9kZWxpbmcvZGF0YS9oczExMV9kZXZfZXllX3JlZl9iY3MuZnVsbC4yMDI1MDIwNC5jc3YuZ3onKSkKI2hzMTExX2Rldl9xdWVyeV9iY3MkYmFyY29kZSAlPiUgd3JpdGUoZ3pmaWxlKCd+L2dpdC9zY0VpYURfbW9kZWxpbmcvZGF0YS9oczExMV9kZXZfZXllX3F1ZXJ5X2Jjcy5mdWxsLjIwMjUwMjA0LmNzdi5neicpKQoKYGBgCgojIyBydW4gc2NWSQoKbm93IGdvIHRvIGJpb3d1bGYyOi9kYXRhL09HVkZCX0JHL3NjRWlhRC8yMDI0XzAyXzI4L3NuYWtlb3V0L2hzMTExX21hdHVyZV9leWUKCmBgYHtiYXNoLCBleGVjID0gRkFMU0V9CmNkIC9kYXRhL09HVkZCX0JHL3NjRWlhRC8yMDI0XzAyXzI4L3NuYWtlb3V0L2hzMTExX2RldmVsb3BpbmdfZXllCnNvdXJjZSAvZGF0YS8kVVNFUi9jb25kYS9ldGMvcHJvZmlsZS5kL2NvbmRhLnNoICYmIHNvdXJjZSAvZGF0YS8kVVNFUi9jb25kYS9ldGMvcHJvZmlsZS5kL21hbWJhLnNoCnNiYXRjaCAtLXRpbWU9ODowMDowMCBzbmFrZWNhbGwuc2gKYGBgCgojIyByc3luYyBvdXRwdXQgZnJvbSBiaW93dWxmMiB0byBsb2NhbCBjb21wdXRlcgpgYGB7YmFzaCwgZXhlYyA9IEZBTFNFfQpjZCAvVXNlcnMvbWNnYXVnaGV5ZC9kYXRhL3NjRWlhRF9tb2RlbGluZy9oczExMV9kZXZlbG9waW5nX2V5ZQpyc3luYyAtUHJhdiBoMjovZGF0YS9PR1ZGQl9CRy9zY0VpYUQvMjAyNF8wMl8yOC9zbmFrZW91dC9oczExMV9kZXZlbG9waW5nX2V5ZS8qb2JzKiAuCmBgYAoKIyBydW4gQ1QgcHJlZGljdGlvbnMgZnJvbSBvdXIgY29sbGF0ZWQgbW9kZWxzCmBgYHtiYXNoLCBleGVjID0gRkFMU0V9CmNkIC9kYXRhL09HVkZCX0JHL3NjRWlhRC8yMDI0XzAyXzI4L3NuYWtlb3V0L2hzMTExX2RldmVsb3BpbmdfZXllCnNvdXJjZSAvZGF0YS8kVVNFUi9jb25kYS9ldGMvcHJvZmlsZS5kL2NvbmRhLnNoICYmIHNvdXJjZSAvZGF0YS8kVVNFUi9jb25kYS9ldGMvcHJvZmlsZS5kL21hbWJhLnNoCm1hbWJhIGFjdGl2YXRlIHJhcGlkc19zaW5nbGVjZWxsCiMgaGFuZCBjdXQgZG93biB0aGUgYWxsIGh1bWFuIGg1YWQgYXMgaXQgd2FzIHVzaW5nIHRvbyBtdWNoIG1lbW9yeSBpbiBweXRob24KIyBpbXBvcnQgc2NhbnB5IGFzIHNjCiMgYmlnX2FkYXRhID0gc2MucmVhZF9oNWFkKCcuLi8uLi9oczExMS5hZGF0YS5zb2xvLjIwMjUwMjA0Lmg1YWQnKQojIGRldl9hZGF0YSA9IHNjLnJlYWRfaDVhZCgnaHMxMTFfZGV2X2V5ZV8yMDI1MDIwNF8yMDAwaHZnXzIwMGVfMzBsLmg1YWQpCiMgbmV3X2FkYXRhID0gYmlnX2FkYXRhW2Rldl9hZGF0YS5vYnNfbmFtZXMsOl0KIyBuZXdfYWRhdGEud3JpdGVfaDVhZCgic25ha2VvdXQvaHMxMTFfZGV2ZWxvcGluZ19leWUvaHMxMTEuYWRhdGEuc29sby4yMDI1MDIwNC5kZXYuaDVhZCIpCnB5dGhvbiB+L2dpdC9zY0VpYURfbW9kZWxpbmcvd29ya2Zsb3cvc2NyaXB0cy9jdF9wcm9qZWN0aW9uLnB5ICBoczExMS5hZGF0YS5zb2xvLjIwMjUwMjA0LmRldi5oNWFkIG1vZGVsc19odW1hbi50c3YgY3RfcHJlZGljdGlvbnNfX2hzMTExLmFkYXRhLnNvbG8uMjAyNTAyMDQuY3N2Lmd6CmBgYAoKIyBTdGFnZSAzCgojIyBBc3Nlc3MgT3V0cHV0CmBgYHtyfQpzb3VyY2UoJ2FuYWx5c2lzX3NjcmlwdHMuUicpCgpvYnMgPC0gcHVsbF9vYnMoJ34vZGF0YS9zY0VpYURfbW9kZWxpbmcvaHMxMTFfZGV2ZWxvcGluZ19leWUvaHMxMTFfZGV2X2V5ZV8yMDI1MDIwNF8yMDAwaHZnXzIwMGVfMzBsLm9icy5jc3YuZ3onLCBtYWNoaW5lX2xhYmVsID0gJ3NjQU5WSV9NQ1QnKQoKY3RfcHJlZGljdGlvbnMgPC0gZGF0YS50YWJsZTo6ZnJlYWQoIn4vZGF0YS9zY0VpYURfbW9kZWxpbmcvaHMxMTFfZGV2ZWxvcGluZ19leWUvY3RfcHJlZGljdGlvbnNfX2hzMTExLmFkYXRhLnNvbG8uMjAyNTAyMDQuY3N2Lmd6IikgJT4lIHNlbGVjdCgtMTcpCgpvYnMkb2JzIDwtIG9icyRvYnMgJT4lIGxlZnRfam9pbihjdF9wcmVkaWN0aW9ucyAlPiUgc2VsZWN0KGJhcmNvZGUsIENUX19jaGVuX2ZldGFsX2hyY2EsIHVtYXAxX2NoZW5fZmV0YWxfaHJjYSx1bWFwMl9jaGVuX2ZldGFsX2hyY2EsIENUX19zY2VpYWRfMjAyNTAxMDdfZnVsbCksIGJ5ID0gYygiYmFyY29kZWkiID0gJ2JhcmNvZGUnKSkKYGBgCgojIyBVTUFQcwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpvYnMkb2JzICU+JSAKICBsZWZ0X2pvaW4ob2JzJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBzY0FOVklfTUNUKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDAuNSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KHNjQU5WSV9NQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBzY0FOVklfTUNULCBjb2xvciA9IHNjQU5WSV9NQ1QpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGdndGl0bGUoIk1ham9yQ2VsbFR5cGUgKHNjQU5WSSkiKQoKb2JzJG9icyAlPiUKICBmaWx0ZXIoTWFqb3JDZWxsVHlwZSAhPSAndW5sYWJlbGxlZCcpICU+JSAKICBtdXRhdGUoTWFqb3JDZWxsVHlwZSA9IGNhc2Vfd2hlbihTdWJDZWxsVHlwZSA9PSAnTlJQQycgfiAnbmV1cm9nZW5pYycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IE1ham9yQ2VsbFR5cGUpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gTWFqb3JDZWxsVHlwZSksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAwLjUpICsKICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShNYWpvckNlbGxUeXBlKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gTWFqb3JDZWxsVHlwZSwgY29sb3IgPSBNYWpvckNlbGxUeXBlKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmdsYXNiZXkoKSxwYWxzOjphbHBoYWJldDIoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGdndGl0bGUoIk1ham9yQ2VsbFR5cGUiKQoKb2JzJG9icyAlPiUKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBDVF9fY2hlbl9mZXRhbF9ocmNhKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDAuNSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KENUX19jaGVuX2ZldGFsX2hyY2EpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBDVF9fY2hlbl9mZXRhbF9ocmNhLCBjb2xvciA9IENUX19jaGVuX2ZldGFsX2hyY2EpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSwgcGFsczo6YWxwaGFiZXQoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGdndGl0bGUoIkNUX19jaGVuX2ZldGFsX2hyY2EiKQoKb2JzJG9icyAlPiUKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBDVF9fc2NlaWFkXzIwMjUwMTA3X2Z1bGwpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkoQ1RfX3NjZWlhZF8yMDI1MDEwN19mdWxsKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gQ1RfX3NjZWlhZF8yMDI1MDEwN19mdWxsLCBjb2xvciA9IENUX19zY2VpYWRfMjAyNTAxMDdfZnVsbCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpLCBwYWxzOjphbHBoYWJldCgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ2d0aXRsZSgiQ1RfX3NjZWlhZF8yMDI1MDEwN19mdWxsIikKCmBgYAojIyMgQnkgbGVpZGVuMwoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0Kb2JzJG9icyAlPiUKICBsZWZ0X2pvaW4ob2JzJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBhcy5mYWN0b3IobGVpZGVuMykpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShtQ1QsIGxlaWRlbjMpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSwpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBwYXN0ZTAobUNULCctJyxsZWlkZW4zKSksIGJnLmNvbG9yID0gJ3doaXRlJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCksIHBhbHM6OmFscGhhYmV0KCksIHBhbHM6OmtlbGx5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBnZ3RpdGxlKCJsZWlkZW4zIikgCmBgYAojIyMgQmlwb2xhciAvIHJvZCBwcmVjdXJzb3IgaXNzdWUKVGhpcyByZXByZXNlbnRhdGlvbiBoYXMgYSBiaWcgImJsb2IiIChjbHVzdGVycyAxMSwgMzEpIHdoaWNoIGFyZSBhbiBhbWFsZ2FtYXRpb24gb2YgbnJwYyAobmV1cm9nZW5pYykgLyByb2QgcHJlY3Vyc29yIC8gYmlwb2xhciBwcmVjdXJzb3IgY2VsbHMuIFB1bGxpbmcgaW4gYW5vdGhlciBzY1ZJIHJlcHJlc2VudGF0aW9uICh3aXRoIGZld2VyIGVwb2NocyBhbmQgbW9yZSBsYXRlbnQgZGltZW5zaW9ucyB3aGljaCwgZW1waXJpY2FsbHksIHRlbmRzIHRvIG1vcmUgY2xlYXJseSBkaXN0aW5ndWlzaCBkaWZmZXJlbnQgY2VsbCB0eXBlcykuCgpUaGlzIHJlcHJlc2VudGF0aW9uIGJldHRlciBkaXN0aW5ndWlzaGVzIG5ycGMgKDIyKSwgcm9kIHByZWN1cnNvciAoMjApLCBhbmQgYmlwb2xhciBwcmVjdXJzb3IgKDQ1KQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpvYnM1MCA8LSBwdWxsX29icygnfi9kYXRhL3NjRWlhRF9tb2RlbGluZy9oczExMV9kZXZlbG9waW5nX2V5ZS9oczExMV9kZXZfZXllXzIwMjUwMjA0XzIwMDBodmdfNTBlXzUwbC5vYnMuY3N2Lmd6JywgbWFjaGluZV9sYWJlbCA9ICdzY0FOVklfTUNUJykKCm9iczUwJG9icyAlPiUgCiAgbGVmdF9qb2luKG9iczUwJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBzY0FOVklfTUNUKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDAuNSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KHNjQU5WSV9NQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBzY0FOVklfTUNULCBjb2xvciA9IHNjQU5WSV9NQ1QpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6Z2xhc2JleSgpLHBhbHM6OmFscGhhYmV0MigpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ2d0aXRsZSgiTWFqb3JDZWxsVHlwZSAoc2NBTlZJKSIpCgpvYnM1MCRvYnMgJT4lCiAgbGVmdF9qb2luKG9iczUwJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBhcy5mYWN0b3IobGVpZGVuMykpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSAuICU+JSBncm91cF9ieShtQ1QsIGxlaWRlbjMpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSwpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBwYXN0ZTAobUNULCctJyxsZWlkZW4zKSksYmcuY29sb3IgPSAnd2hpdGUnKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSwgcGFsczo6YWxwaGFiZXQoKSwgcGFsczo6a2VsbHkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGdndGl0bGUoImxlaWRlbjMiKSAKCm9iczUwJG9icyAlPiUKICBsZWZ0X2pvaW4ob2JzNTAkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGZpbHRlcihsZWlkZW4zICVpbiUgYygyMiwyMCw0NSkpICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBhcy5mYWN0b3IobGVpZGVuMykpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkobUNULCBsZWlkZW4zKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSwpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gcGFzdGUwKG1DVCwnLScsbGVpZGVuMykpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSwgcGFsczo6YWxwaGFiZXQoKSwgcGFsczo6a2VsbHkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGdndGl0bGUoImxlaWRlbjMiKSAKCm9icyRvYnMgJT4lIGxlZnRfam9pbihvYnM1MCRvYnMgJT4lIHNlbGVjdChiYXJjb2RlaSwgbGVpZGVuM181MCA9IGxlaWRlbjMpLCBieSA9ICdiYXJjb2RlaScpICU+JSBncm91cF9ieShsZWlkZW4zLCBsZWlkZW4zXzUwKSAlPiUgc3VtbWFyaXNlKENvdW50ID0gbigpKSAlPiUgbXV0YXRlKFJhdGlvID0gQ291bnQvc3VtKENvdW50KSkgJT4lIGZpbHRlcihsZWlkZW4zICVpbiUgYygxMSwzMSksIFJhdGlvID4gMC4xKQpgYGAKCiMjIyMgb2JzIGxlaWRlbjMgPC0+IG9ic181MCBsZWlkZW4zCmBgYHtyfQpvYnMkb2JzICU+JSAKICBsZWZ0X2pvaW4ob2JzNTAkb2JzICU+JSBzZWxlY3QoYmFyY29kZWksIGxlaWRlbjNfNTAgPSBsZWlkZW4zKSwgYnkgPSAnYmFyY29kZWknKSAlPiUgCiAgZ3JvdXBfYnkobGVpZGVuMywgbGVpZGVuM181MCkgJT4lIAogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lIAogIG11dGF0ZShSYXRpbyA9IENvdW50L3N1bShDb3VudCkpICU+JSAKICBmaWx0ZXIoUmF0aW8gPiAwLjEpICU+JQogIG11dGF0ZShsZWlkZW4zID0gYXMuZmFjdG9yKGxlaWRlbjMpLAogICAgICAgICBsZWlkZW4zXzUwID0gYXMuZmFjdG9yKGxlaWRlbjNfNTApKSAlPiUgCiAgRFQ6OmRhdGF0YWJsZShmaWx0ZXIgPSAndG9wJykKYGBgCgoKCiMjIFRhYmxlcwpgYGB7cn0Kb2JzJG9icyA8LSBvYnMkb2JzICU+JSAKICBtdXRhdGUoQ1RfX2NoZW5fZmV0YWxfaHJjYV9jb3JlID0gY2FzZV93aGVuKGdyZXBsKCJBQ1xcZCIsQ1RfX2NoZW5fZmV0YWxfaHJjYSkgfiAnYW1hY3JpbmUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ1RfX2NoZW5fZmV0YWxfaHJjYSA9PSAnTUcnIH4gJ211ZWxsZXInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ1RfX2NoZW5fZmV0YWxfaHJjYSAlaW4lIGMoIkJCX0dCIiwgIkZNQiIsICJJTUIiKSB+ICdiaXBvbGFyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJEQlxcZCIsQ1RfX2NoZW5fZmV0YWxfaHJjYSkgfiAnYmlwb2xhcicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiT0ZGfE9OIiwgQ1RfX2NoZW5fZmV0YWxfaHJjYSkgfiAncmV0aW5hbCBnYW5nbGlvbicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDVF9fY2hlbl9mZXRhbF9ocmNhID09ICdTX0NvbmUnIH4gJ2NvbmUgKHMpJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENUX19jaGVuX2ZldGFsX2hyY2EgPT0gJ01MX0NvbmUnIH4gJ2NvbmUgKG1sKScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDVF9fY2hlbl9mZXRhbF9ocmNhID09ICdSQkMnIH4gJ3JlZCBibG9vZCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDVF9fY2hlbl9mZXRhbF9ocmNhID09ICdSR0MgUHJlY3Vyc29yJyB+ICdyZXRpbmFsIGdhbmdsaW9uIHByZWN1cnNvcicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDVF9fY2hlbl9mZXRhbF9ocmNhID09ICdCQyBQcmVjdXJzb3InIH4gJ2JpcG9sYXIgcHJlY3Vyc29yJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENUX19jaGVuX2ZldGFsX2hyY2EgPT0gJ0FDIFByZWN1cnNvcicgfiAnYW1hY3JpbmUgcHJlY3Vyc29yJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENUX19jaGVuX2ZldGFsX2hyY2EgPT0gJ0hDIFByZWN1cnNvcicgfiAnaG9yaXpvbnRhbCBwcmVjdXJzb3InLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IHRvbG93ZXIoQ1RfX2NoZW5fZmV0YWxfaHJjYSkpKQoKb2JzJG9icyAlPiUgCiAgZ3JvdXBfYnkobGVpZGVuMywgQ1RfX2NoZW5fZmV0YWxfaHJjYV9jb3JlKSAlPiUgCiAgc3VtbWFyaXNlKENvdW50ID0gbigpLCBBZ2UgPSBtZWFuKGFnZSkpICU+JSAKICBsZWZ0X2pvaW4ob2JzJG9icyAlPiUgZ3JvdXBfYnkobGVpZGVuMykgJT4lIHN1bW1hcmlzZShUb3RhbCA9IG4oKSkpICU+JSAKICBtdXRhdGUoUmF0aW8gPSBDb3VudCAvIFRvdGFsKSAlPiUgCiAgZmlsdGVyKFJhdGlvID4gMC4wMSkgJT4lIGFycmFuZ2UobGVpZGVuMywgLVJhdGlvKSAlPiUgCiAgbXV0YXRlKGxlaWRlbjMgPSBhcy5mYWN0b3IobGVpZGVuMykpICU+JSAKICBEVDo6ZGF0YXRhYmxlKGZpbHRlciA9ICd0b3AnKQoKYGBgCgoKYGBge3J9Cm9icyRvYnMgJT4lIAogIGdyb3VwX2J5KGxlaWRlbjMsIHNjQU5WSV9NQ1QsIENUX19jaGVuX2ZldGFsX2hyY2FfY29yZSwgQ1RfX3NjZWlhZF8yMDI1MDEwN19mdWxsKSAlPiUgCiAgc3VtbWFyaXNlKENvdW50ID0gbigpLCBBZ2UgPSBtZWFuKGFnZSkpICU+JSAKICBsZWZ0X2pvaW4ob2JzJG9icyAlPiUgZ3JvdXBfYnkobGVpZGVuMykgJT4lIHN1bW1hcmlzZShUb3RhbCA9IG4oKSkpICU+JSAKICBtdXRhdGUoUmF0aW8gPSBDb3VudCAvIFRvdGFsKSAlPiUgCiAgZmlsdGVyKFJhdGlvID4gMC4wMSkgJT4lIGFycmFuZ2UobGVpZGVuMywgLVJhdGlvKSAlPiUgCiAgbXV0YXRlKGxlaWRlbjMgPSBhcy5mYWN0b3IobGVpZGVuMykpICU+JSAKICBEVDo6ZGF0YXRhYmxlKGZpbHRlciA9ICd0b3AnKQoKYGBgCgoKIyBIYW5kIExhYmVsIENoYW5nZXMKRmlyc3QgdGFrZSB0aGUgY2hlbiBjZWxsIGxhYmVscywgdGhlbiBoYW5kIGFsdGVyIGFueXRoaW5nIHRoYXQgbmVlZHMgZml4aW5nCmBgYHtyfQpsYWJlbHMgPC0gb2JzJG9icyAlPiUgCiAgbXV0YXRlKENUX19jaGVuX2ZldGFsX2hyY2FfY29yZSA9IGdzdWIoInByZWN1cnNvciIsIihwcmVjdXJzb3IpIiwgQ1RfX2NoZW5fZmV0YWxfaHJjYV9jb3JlKSkgJT4lIAogIGdyb3VwX2J5KGxlaWRlbjMsIENUX19jaGVuX2ZldGFsX2hyY2FfY29yZSkgJT4lIAogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lIAogIHNsaWNlX21heChvcmRlcl9ieSA9IENvdW50LCBuID0gMSkgJT4lIAogIG11dGF0ZShDVF9fY2hlbl9mZXRhbF9ocmNhX2NvcmUgPSBjYXNlX3doZW4oQ1RfX2NoZW5fZmV0YWxfaHJjYV9jb3JlID09ICducnBjJyB+ICduZXVyb2dlbmljJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBDVF9fY2hlbl9mZXRhbF9ocmNhX2NvcmUpKQoKbGFiZWxfY2hhbmdlIDwtIHJiaW5kKAogIGMoOSwgJ2ZpYnJvYmxhc3QnKSwKICBjKDExLCAnYmlwb2xhciAocHJlY3Vyc29yKScpLAogIGMoMTgsICdhbWFjcmluZSAocHJlY3Vyc29yKScpLAogIGMoMTksICdmaWJyb2JsYXN0JyksCiAgYygzMSwgJ2JpcG9sYXIgKHByZWN1cnNvciknKSwKICBjKDMyLCAncmV0aW5hbCBnYW5nbGlvbiAocHJlY3Vyc29yKScpLAogIGMoMzMsICdmaWJyb2JsYXN0JyksCiAgYygzNiwgJ2FtYWNyaW5lIChwcmVjdXJzb3IpJyksCiAgYygzNSwgJ2hvcml6b250YWwnKSwKICBjKDQwLCAncm9kIChwcmVjdXJzb3IpJyksCiAgYyg1MSwgJ3JvZCAocHJlY3Vyc29yKScpLAogIGMoNTcsICdmaWJyb2JsYXN0JyksCiAgYyg1MCwgJ2hvcml6b250YWwnKSwKICBjKDU4LCAna2VyYXRvY3l0ZScpLAogIGMoNjIsICdmaWJyb2JsYXN0JyksCiAgYyg2NSwgJ2hvcml6b250YWwnKSwKICBjKDY2LCAnZmlicm9ibGFzdCcpLAogIGMoNjgsICdhc3Ryb2N5dGUnKSwKICBjKDcwLCAnbWljcm9nbGlhJyksCiAgYyg3NCwgJ2tlcmF0b2N5dGUnKSwKICBjKDc1LCAncm9kJyksCiAgYyg3NiwgJ2VuZG90aGVsaWFsJyksCiAgYyg3OCwgJ3JlZCBibG9vZCcpLAogIGMoODEsICdycGUnKSwKICBjKDgyLCAnZmlicm9ibGFzdCcpLAogIGMoODYsICdyZWQgYmxvb2QnKSwKICBjKDg3LCAnbXVzY2xlIChjaWxpYXJ5KScpLAogIGMoODgsICdiaXBvbGFyIChwcmVjdXJzb3IpJyksCiAgYyg4OSwgJ25ldXJvZ2VuaWMnKSwKICBjKDkwLCAnZXBpdGhlbGlhbCcpLAogIGMoOTIsICdiaXBvbGFyJykpICU+JSBhc190aWJibGUoKSAlPiUgCiAgbXV0YXRlKFYxID0gYXMuaW50ZWdlcihWMSkpICU+JSAKICBkcGx5cjo6cmVuYW1lKENUID0gVjIsIGxlaWRlbjMgPSBWMSkKCmxhYmVscyA8LSBsYWJlbHMgJT4lIGxlZnRfam9pbihsYWJlbF9jaGFuZ2UpICU+JSAKICBtdXRhdGUoQ1QgPSBjYXNlX3doZW4oaXMubmEoQ1QpIH4gQ1RfX2NoZW5fZmV0YWxfaHJjYV9jb3JlLAogICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gQ1QpKQoKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTh9Cm9icyRvYnMgJT4lIAogIGxlZnRfam9pbihsYWJlbHMsIGJ5ID0gJ2xlaWRlbjMnKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkoQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBDVCwgY29sb3IgPSBDVCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKCgpvYnMkb2JzICU+JSAKICBsZWZ0X2pvaW4ob2JzJGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBsZWZ0X2pvaW4obGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IGFzLmZhY3RvcihsZWlkZW4zKSksIHBvaW50c2l6ZSA9IDAuOCwgYWxwaGEgPSAwLjUpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KG1DVCwgbGVpZGVuMykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHBhc3RlMChtQ1QsICcgLSAnLCBsZWlkZW4zKSksIGJnLmNvbG9yID0gJ3doaXRlJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCksIHBhbHM6OmFscGhhYmV0KCksIHBhbHM6OmtlbGx5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgoKYGBgCgoKCgojIyBoY2x1c3QKVGFrZSBwc2V1ZG9idWxrIHZhbHVlcyAoYXQgdGhlIGNsdXN0ZXIgbGV2ZWwpIGFuZCBoaWVyYXJjaGljYWxseSBjbHVzdGVyIHRoZW0gdG8gZW5zdXJlIAp0aGVyZSBhcmVuJ3QgYW55IGlzc3VlcyBpbiBlaXRoZXIgdGhlIG92ZXJhbGwgc3RydWN0dXJlIChlLmcuIHJvZCBhbmQgY29uZXMgYXJlIGludGVyc3BlcnNlKWQKYW5kL29yIHRvIGlkZW50aWZ5IGFueSBwb3RlbnRpYWwgbWlzbGFiZWxlZCBjbHVzdGVycwoKYGBge3IsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gMTB9CnBiIDwtIGRhdGEudGFibGU6OmZyZWFkKCd+L2RhdGEvc2NFaWFEX21vZGVsaW5nL2hzMTExX2RldmVsb3BpbmdfZXllL2hzMTExX2Rldl9leWVfMjAyNTAyMDRfMjAwMGh2Z18yMDBlXzMwbC5wc2V1ZG9CdWxrLmxlaWRlbjMuY3N2Lmd6JykKY29sbmFtZXMocGIpIDwtIGdzdWIoIlxcLlxcZCsiLCIiLGNvbG5hbWVzKHBiKSkKaHZnIDwtIGRhdGEudGFibGU6OmZyZWFkKCd+L2RhdGEvc2NFaWFEX21vZGVsaW5nL2hzMTExX2RldmVsb3BpbmdfZXllL2h2ZzIwMDAuY3N2Lmd6JylbLTEsXQpybmFtZXMgPC0gcGIkVjEKY2x1c3QgPC0gc3RyX2V4dHJhY3Qocm5hbWVzLCAnXFxkKycpICU+JSBhcy5pbnRlZ2VyKCkKcGIgPC0gcGJbLC0xXSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKHBiKSA8LSBhcy5jaGFyYWN0ZXIoY2x1c3QpCnBiIDwtIHBiW2FzLmNoYXJhY3RlcihvYnMkbGFiZWxzJGxlaWRlbjMpLF0KCnBiX25vcm0gPC0gbWV0YW1vUnBoOjpub3JtYWxpemVfZGF0YSh0KHBiKSwgc2FtcGxlX3NjYWxlID0gJ2NwbScpICU+JSB0KCkgCgpwYl9ub3JtIDwtIHBiX25vcm1bLGh2ZyRWMl0KI3BiX25vcm0gPC0gcGJfbm9ybVssaHZnJFYyWyFodmckVjIgJWluJSBjKGNjX2dlbmVzLHJpYm9fZ2VuZXMpXV0KIyBodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8zMTU2NS9jb21wdXRlLWEtY29zaW5lLWRpc3NpbWlsYXJpdHktbWF0cml4LWluLXIKc2ltIDwtIHBiX25vcm0gLyBzcXJ0KHJvd1N1bXMocGJfbm9ybSAqIHBiX25vcm0pKQpzaW0gPC0gc2ltICUqJSB0KHNpbSkKRF9zaW0gPC0gYXMuZGlzdCgxIC0gc2ltKQoKaGNsdXN0X3NpbSA8LSBoY2x1c3QoRF9zaW0sIG1ldGhvZCA9ICdhdmVyYWdlJykKCmhjbHVzdF9zaW0kbGFiZWxzIDwtIG9icyRsYWJlbHMgJT4lIHB1bGwobGVpZGVuMykKCmxpYnJhcnkoZ2d0cmVlKQpwIDwtIGdndHJlZShoY2x1c3Rfc2ltKQpwJGRhdGEgPC0gcCRkYXRhICU+JSBsZWZ0X2pvaW4obGFiZWxzLCBieSA9IGMoImxhYmVsIiA9ICJsZWlkZW4zIikpCnAgKyBsYXlvdXRfZGVuZHJvZ3JhbSgpICsKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSBwYXN0ZShsYWJlbCwgQ1QsIHNlcCA9ICcgLSAnKSwgY29sb3IgPSBDVCkpICsgCiAgdGhlbWVfZGVuZHJvZ3JhbShwbG90Lm1hcmdpbj1tYXJnaW4oMTYsMTYsMzAwLDE2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBndWlkZXMoY29sb3I9Im5vbmUiKQoKCgpwIDwtIGdndHJlZShoY2x1c3Rfc2ltKQpwJGRhdGEgPC0gcCRkYXRhICU+JSAKICBsZWZ0X2pvaW4obGFiZWxzLCBieSA9IGMoImxhYmVsIiA9ICJsZWlkZW4zIikpICU+JSAKICBsZWZ0X2pvaW4ob2JzJGxhYmVscyAlPiUgbXV0YXRlKHN0dWRpZXMgPSBjYXNlX3doZW4oc3R1ZHlDb3VudCA9PTEgfiBzdHVkaWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIm11bHRpcGxlIikpLCBieSA9IGMoImxhYmVsIiA9ICJsZWlkZW4zIikpIAoKcCArIGxheW91dF9kZW5kcm9ncmFtKCkgKwogIGdlb21fdGlwbGFiKGFlcyhsYWJlbCA9IHBhc3RlKGxhYmVsLCBDVCwgc3R1ZGllcywgc2VwID0gJyAtICcpLCBjb2xvciA9IENUKSkgKyAKICBnZW9tX3RpcHBvaW50KGFlcyhzaGFwZSA9IHN0dWRpZXMpLCBzaXplPSAzKSArCiAgdGhlbWVfZGVuZHJvZ3JhbShwbG90Lm1hcmdpbj1tYXJnaW4oMTYsMTYsMzAwLDE2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkgKyAKICBndWlkZXMoY29sb3I9Im5vbmUiKQoKCgpgYGAKCgojIyBSZW1vdmUgQ2x1c3RlcnMKCkNvbWJpbmF0aW9uIG9mIHRoZXNlIHJlYXNvbnM6Ci0gbm9uLW5ldXJhbCB3aXRoIG5ldXJhbAotIGxvdyBuIGNsdXN0ZXJzIENUIGZhciBhcGFydCBmcm9tIHNhbWUgQ1QKLSBzdHVkeSBzcGVjaWZpYwotIHVtYXAgbG9va3MgInN0cmFuZ2UiIAoKYGBge3J9CnJlbW92ZV9sZWlkZW4zIDwtIGMoMzIsIDQyLCA2NiwgODIsODgsIDkxLCA5MiwgOTgsIDk5LAogICAgICAgICAgICAgICAgICAgIDQwLCA1MSApICMgY29tYm8gb2Yga2VyYXRpbi9yaG8gZXhwcmVzc2lvbi4uLgoKYGBgCgojIENUIGJ5IENUCmBgYHtyfQpkaWZmIDwtIHB1bGxfZGlmZigifi9kYXRhL3NjRWlhRF9tb2RlbGluZy9oczExMV9kZXZlbG9waW5nX2V5ZS9oczExMV9kZXZfZXllXzIwMjUwMjA0XzIwMDBodmdfMjAwZV8zMGwuZGlmZnRlc3RpbmcubGVpZGVuMy5jc3YuZ3oiKQoKY29udl90YWJsZSA8LSBBbm5vdGF0aW9uRGJpOjpzZWxlY3Qob3JnLkhzLmVnLmRiOjpvcmcuSHMuZWcuZGIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleXM9Z3N1YignXFwuXFxkKycsJycsdW5pcXVlKGRpZmYkZGlmZl90ZXN0aW5nJEVOU0VNQkwpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1ucz1jKCJFTlNFTUJMIiwiU1lNQk9MIiwgIk1BUCIsIkdFTkVOQU1FIiwgIkVOVFJFWklEIiksIGtleXR5cGU9IkVOU0VNQkwiKQoKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQoKaG1fbWFrZXIgPC0gZnVuY3Rpb24obWFya2VycywgdGFyZ2V0LCAKICAgICAgICAgICAgICAgICAgICAgY2RpZmYgPSBkaWZmLCAKICAgICAgICAgICAgICAgICAgICAgY2xhYmVscyA9IGxhYmVscywgCiAgICAgICAgICAgICAgICAgICAgIHJlbW92ZSA9IHJlbW92ZV9sZWlkZW4zKXsKICB0aWIgPC0gY2RpZmYkZGlmZl90ZXN0aW5nICU+JSAKICAgIGxlZnRfam9pbihjbGFiZWxzLCBieSA9IGMoJ2Jhc2UnPSdsZWlkZW4zJykpICU+JSAKICAgIGxlZnRfam9pbihjb252X3RhYmxlICU+JSBzZWxlY3QoU1lNQk9MLCBFTlNFTUJMKSAlPiUgdW5pcXVlKCkpICU+JSAKICAgIGZpbHRlcihTWU1CT0wgJWluJSBtYXJrZXJzKSAlPiUgCiAgICBtdXRhdGUoYmFzZSA9IGFzLmNoYXJhY3RlcihiYXNlKSwKICAgICAgICAgICBiYXNlID0gcGFzdGUwKGJhc2UsICcgLSAnLCBDVCkpICU+JSAKICAgIHNlbGVjdChTWU1CT0wsIGJhc2UsIGxvZ2ZvbGRjaGFuZ2VzKSAlPiUgCiAgICBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IGxvZ2ZvbGRjaGFuZ2VzLCBuYW1lc19mcm9tID0gYmFzZSkKICAKICBtYXQgPC0gdGliICU+JSBzZWxlY3QoLTEpICU+JSBhcy5tYXRyaXgoKQogIHJvdy5uYW1lcyhtYXQpIDwtIHRpYiAlPiUgcHVsbCgxKQogIAogIGhhX2NvbHVtbiA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbihkZiA9IGRhdGEuZnJhbWUoVGFyZ2V0ID0gaWZlbHNlKGdyZXBsKHRhcmdldCwgY29sbmFtZXModGliKVstMV0pLCAiVGFyZ2V0IiwiTm90IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZW1vdmUgPSBpZmVsc2Uoc3RyX2V4dHJhY3QoY29sbmFtZXModGliKVstMV0sICdcXGQrJykgJWluJSByZW1vdmUsICJSZW1vdmUiLCJSZXRhaW4iKSksICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChUYXJnZXQgPSBjKCJUYXJnZXQiID0gImJsYWNrIiwiTm90IiA9ICJ3aGl0ZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlbW92ZSA9IGMoIlJlbW92ZSIgPSAicmVkIiwgIlJldGFpbiIgPSAid2hpdGUiKSkpCiAgCiAgY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoLTYsIDAsIDYpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQogIGRyYXcoSGVhdG1hcChtYXQsIGNvbD1jb2xfZnVuLAogICAgICAgICAgICAgICBuYW1lID0gJ2xvZ0ZvbGRDaGFuZ2UnLAogICAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhhX2NvbHVtbikKICApCn0KYGBgCgoKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQptYXJrZXJzIDwtIGMoJ1BSS0NBJywnR1JNNicsJ0dSSUsxJykKdGFyZ2V0IDwtICJiaXBvbGFyIgpobV9tYWtlcihtYXJrZXJzLCB0YXJnZXQpCmBgYAojIyBSUEMgLyBOZXVyb2dlbmljCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9N30KIyBtYXJrZXJzIDwtIGMoIkhFUzEiLAojICAgICAgICAgICAgICAiWkZQMzZMMiIsCiMgICAgICAgICAgICAgICMgIkhFUzYiLAojICAgICAgICAgICAgICAjICJBVE9INyIsCiMgICAgICAgICAgICAgICJWSU0iLAojICAgICAgICAgICAgICAiQ0NORDEiLAojICAgICAgICAgICAgICAiU0ZSUDIiLAojICAgICAgICAgICAgICAiU1BQMSIsCiMgICAgICAgICAgICAgICJaRlAzNkwxIiwKIyAgICAgICAgICAgICAgIlRGIiwKIyAgICAgICAgICAgICAgIkZPUyIsCiMgICAgICAgICAgICAgICJUVFlIMSIpCm1lbGxvdWdoX21hcmtlcnMgPC0gcmVhZF9jc3YoIn4vZ2l0L2V5ZU1hcmtlcnMvbGlzdHMvcnBjX21hcmtlcnNfX01lbGxvdWdoMjAxOS5jc3YiKQoKbWFya2VycyA8LSBtZWxsb3VnaF9tYXJrZXJzICU+JSBmaWx0ZXIoYENlbGwgVHlwZWAgPT0gJ1JQQycpICU+JSBwdWxsKEhHTkMpCmhtX21ha2VyKGMobWFya2VycywgIlBBWDYiLCJORVVST0QxIiwiQVRPSDciLCJIRVM2IiksICJycGN8bmV1cm8iKQoKYGBgCgojIyBCaXBvbGFyCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9NX0KbWFya2VycyA8LSBjKCJHUklLMSIsIklSWDYiLCJMUlRNMSIsIlBDUDIiLCJQUktDQSIsIlRSUE0xIiwiVlNYMSIsIlZTWDIiKQojbWFya2VycyA8LSBtZWxsb3VnaF9tYXJrZXJzICU+JSBmaWx0ZXIoYENlbGwgVHlwZWAgPT0gJ0JpcG9sYXInKSAlPiUgcHVsbChIR05DKQpobV9tYWtlcihtYXJrZXJzLCAiYmlwb2xhciIpCgpgYGAKCiMjIEZpYnJvYmxhc3QgLyBFbmRvIC8gRXBpIC8gS2VyYXRvY3l0ZQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTZ9Cm1hcmtlcnMgPC0gYygiTFVNIiwiRENOIiwiVklNIiwiUERHRlJBIiwiQ09MMUEyIiwgIyBodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MjAwMy0wMjAtMDkyMi00CiAgICAgICAgICAgICAiTUdQIiwiTUVHMyIsIkRDTiIsIkFQT0QiLCJBTkdQVEw3IiwiRUZFTVAxIiwiQk1QNSIsIlBSUlgxIikKCm1hcmtlcnMgPC0gYygiVklNIiwiRkFQIiwiQ09MMUExIiwiUERHRlJCIiwiUzEwMEE0IiwgIyBmaWJybwogICAgICAgICAgICAgIkNESDUiLCJWV0YiLCAjIGVuZG8KICAgICAgICAgICAgICJDREgxIiwiS1JUMTkiLCJFUENBTSIsICMgZXBpCiAgICAgICAgICAgICAiS0VSQSIsIyBrZXJhdG9jeXRlKQogICAgICAgICAgICAgIkEyTSIpIApobV9tYWtlcihtYXJrZXJzLCAiZmlicm9ibGFzdHxlbmRvfGVwaXxrZXJhIikKYGBgCgoKCgojIyBSUEUKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQoKbWFya2VycyA8LSBjKCJQTUVMIiwiVFlSUDEiLCJSUEU2NSIsIkJFU1QxIiwiRENUIikKCmhtX21ha2VyKG1hcmtlcnMsICJycGUiKQoKYGBgCgojIyBhc3Ryb2N5dGUKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQoKbWFya2VycyA8LSAgIGMoIkdGQVAiLCAnUEFYMicpCiNtYXJrZXJzIDwtIG1lbGxvdWdoX21hcmtlcnMgJT4lIGZpbHRlcihgQ2VsbCBUeXBlYCA9PSAnQXN0cm9jeXRlcycpICU+JSBwdWxsKEhHTkMpCmhtX21ha2VyKG1hcmtlcnMsICJhc3Ryb2N5dGUiKQoKYGBgCgojIyBIb3Jpem9udGFsCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9NX0KCm1hcmtlcnMgPC0gYygiTEhYMSIsIklTTDEiLCJPTkVDVVQxIikKCmhtX21ha2VyKG1hcmtlcnMsICJob3JpIikKCmBgYAoKIyMgQW1hY3JpbmUKYGBge3IsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD01fQoKbWFya2VycyA8LSBjKCdHQUQxJywnR0FEMicsJ1NMQzZBOScsJ05GSUEnKQptYXJrZXJzIDwtIG1lbGxvdWdoX21hcmtlcnMgJT4lIGZpbHRlcihgQ2VsbCBUeXBlYCA9PSAnQW1hY3JpbmUnKSAlPiUgcHVsbChIR05DKQpobV9tYWtlcihtYXJrZXJzLCAiYW1hY3IiKQoKYGBgCgojIyBHYW5nbGlvbgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTV9CgptYXJrZXJzIDwtICBtZWxsb3VnaF9tYXJrZXJzICU+JSBmaWx0ZXIoYENlbGwgVHlwZWAgPT0gJ1JHQycpICU+JSBwdWxsKEhHTkMpCmhtX21ha2VyKG1hcmtlcnMsICJnYW5nbGlvbiIpCgpgYGAKCiMjIFBob3RvcmVjZXB0b3JzCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9NX0KCm1hcmtlcnMgPC0gIGMoJ0FSUjMnLCdPUE4xTFcnLCdPUE4xU1cnLCdSSE8nLCAnT1BOMU1XJywgJ1JDVlJOJywiQ1JYIiwiUFJPTTEiLCJDTkdBMSIsIlBERTZBIikKI21hcmtlcnMgPC0gIG1lbGxvdWdoX21hcmtlcnMgJT4lIGZpbHRlcihgQ2VsbCBUeXBlYCAlaW4lIGMoJ1JvZCcsJ0NvbmUnKSkgJT4lIHB1bGwoSEdOQykKaG1fbWFrZXIobWFya2VycywgInJvZHxjb25lIikKCmBgYAoKCiMjIEltbXVuZQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTV9CgptYXJrZXJzIDwtICBjKCJMWVZFMSIsIkNEMTYzIiwKICAgICAgICAgICAgICAiQzFRQSIsIkNUU1MiLCJCMk0iLCJITEEtRFBBMSIsIkhMQS1EUEIxIiwgIkhMQS1EUkEiLAogICAgICAgICAgICAgICJDRDI3IiwiQ0Q3OUEiLAogICAgICAgICAgICAgICJDRDIiLAogICAgICAgICAgICAgICJJTDFSTDEiLAogICAgICAgICAgICAgICJIQkIiLCJIQkEiKQoKaG1fbWFrZXIobWFya2VycywgIm1pY3JvZ2xpYXxibG9vZCIpCmBgYAoKCgoKCiMgVXBkYXRlZCBVTUFQClJlbWluZGVyOiBucnBjICgyMiksIHJvZCBwcmVjdXJzb3IgKDIwKSwgYW5kIGJpcG9sYXIgcHJlY3Vyc29yICg0NSkgZnJvbSAib2JzNTAiCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTh9Cm9icyRvYnMgJT4lIAogIGxlZnRfam9pbihsYWJlbHMsIGJ5ID0gJ2xlaWRlbjMnKSAlPiUgCiAgbXV0YXRlKENUID0gY2FzZV93aGVuKGJhcmNvZGVpICVpbiUgKG9iczUwJG9icyAlPiUgZmlsdGVyKGxlaWRlbjMgPT0gMjIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKGJhcmNvZGVpKSkgfiAnbnJwYycsCiAgICAgICAgICAgICAgICAgICAgICAgIGJhcmNvZGVpICVpbiUgKG9iczUwJG9icyAlPiUgZmlsdGVyKGxlaWRlbjMgPT0gMjApICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKGJhcmNvZGVpKSkgfiAncm9kIChwcmVjdXJzb3IpJywKICAgICAgICAgICAgICAgICAgICAgICAgYmFyY29kZWkgJWluJSAob2JzNTAkb2JzICU+JSBmaWx0ZXIobGVpZGVuMyA9PSA0NSkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1bGwoYmFyY29kZWkpKSB+ICdiaXBvbGFyIChwcmVjdXJzb3IpJywKICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IENUKSkgJT4lIAogIGZpbHRlcighbGVpZGVuMyAlaW4lIHJlbW92ZV9sZWlkZW4zKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkoQ1QpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHVtYXAxID0gbWVkaWFuKHVtYXAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVtYXAyID0gbWVkaWFuKHVtYXAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBDVCwgY29sb3IgPSBDVCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKb2JzJG9icyAlPiUgCiAgbGVmdF9qb2luKGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBmaWx0ZXIoIWxlaWRlbjMgJWluJSByZW1vdmVfbGVpZGVuMykgJT4lIAogIGdncGxvdChhZXMoeD11bWFwMSx5PXVtYXAyKSkgKwogIHNjYXR0ZXJtb3JlOjpnZW9tX3NjYXR0ZXJtb3JlKGFlcyhjb2xvciA9IENUKSwgcG9pbnRzaXplID0gMS4xLCBhbHBoYSA9IDAuNSkgKwogICMgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkoQ1QpICU+JSAKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UodW1hcDEgPSBtZWRpYW4odW1hcDEpLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBDVCwgY29sb3IgPSBDVCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpKSAlPiUgdW5uYW1lKCkpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArIGZhY2V0X3dyYXAofkNUKQoKYGBgCgojIFN0YWdlIDQKUmVtYWtlIHRoZSBzY1ZJIG1vZGVscyB3aXRoIHRoZSB1cGRhdGVkIENUIGNhbGxzIChhbmQgY2VsbCByZW1vdmFsKQoKQWxzbyBmaXggdGhlIHNycDUxMDcxMiBOUlBDIGdldHRpbmcgbGFiZWxsZWQgYXMgUlBDIGluc3RlYWQgb2YgbmV1cm9nZW5pYwoKYGBge3J9Cm5vYnMgPC0gb2JzJG9icyAlPiUgCiAgbGVmdF9qb2luKGxhYmVscywgYnkgPSAnbGVpZGVuMycpICU+JSAKICBmaWx0ZXIoIWxlaWRlbjMgJWluJSByZW1vdmVfbGVpZGVuMykgJT4lIAogIG11dGF0ZShDVCA9IGNhc2Vfd2hlbihiYXJjb2RlaSAlaW4lIChvYnM1MCRvYnMgJT4lIGZpbHRlcihsZWlkZW4zID09IDIyKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChiYXJjb2RlaSkpIH4gJ25ldXJvZ2VuaWMnLAogICAgICAgICAgICAgICAgICAgICAgICBiYXJjb2RlaSAlaW4lIChvYnM1MCRvYnMgJT4lIGZpbHRlcihsZWlkZW4zID09IDIwKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChiYXJjb2RlaSkpIH4gJ3JvZCAocHJlY3Vyc29yKScsCiAgICAgICAgICAgICAgICAgICAgICAgIGJhcmNvZGVpICVpbiUgKG9iczUwJG9icyAlPiUgZmlsdGVyKGxlaWRlbjMgPT0gNDUpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKGJhcmNvZGVpKSkgfiAnYmlwb2xhciAocHJlY3Vyc29yKScsCiAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBDVCkpICU+JSAKICBtdXRhdGUoTWFqb3JDZWxsVHlwZSA9IGNhc2Vfd2hlbihTdWJDZWxsVHlwZSA9PSAnTlJQQycgfiAnbmV1cm9nZW5pYycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IE1ham9yQ2VsbFR5cGUpKQoKc2V0LnNlZWQoMjAyNS0wMi0xMCkKcmVmIDwtIG5vYnMgJT4lIGdyb3VwX2J5KHN0dWR5X2FjY2Vzc2lvbiwgQ1QpICU+JSAKICBzbGljZV9zYW1wbGUobiA9IDIwMDAsIHJlcGxhY2UgPSBUUlVFKSAlPiUgCiAgdW5pcXVlKCkKCnF1ZXJ5IDwtIG5vYnMgJT4lIGZpbHRlcighYmFyY29kZWkgJWluJSByZWYkYmFyY29kZWkpCgpyZWYkYmFyY29kZWkgJT4lIHdyaXRlKGd6ZmlsZSgnfi9naXQvc2NFaWFEX21vZGVsaW5nL2RhdGEvaHMxMTFfZGV2X2V5ZV9yZWZfYmNzLmZ1bGwuMjAyNTAyMTEuc3RhZ2U0LmNzdi5neicpKQpxdWVyeSRiYXJjb2RlaSAlPiUgd3JpdGUoZ3pmaWxlKCd+L2dpdC9zY0VpYURfbW9kZWxpbmcvZGF0YS9oczExMV9kZXZfZXllX3F1ZXJ5X2Jjcy5mdWxsLjIwMjUwMjExLnN0YWdlNC5jc3YuZ3onKSkKCm5vYnMgJT4lIGRwbHlyOjpyZW5hbWUoYmFyY29kZSA9IGJhcmNvZGVpKSAlPiUgd3JpdGVfY3N2KCd+L2dpdC9zY0VpYURfbW9kZWxpbmcvZGF0YS9IdW1hbl9EZXZlbG9waW5nX0V5ZV9fc3RhZ2U0X0NUY2FsbHMuZnJlZXplMjAyNTAyMTEuY3N2Lmd6JykKYGBgCgpBcHBseSBuZXcgQ1QgY2FsbHMgdG8gYSBuZXcgaDVhZApgYGB7YmFzaCBiaW93dWxmMi1hcHBseS1jdCwgZXZhbCA9IEZBTFNFfQoKY2QgL2RhdGEvT0dWRkJfQkcvc2NFaWFELzIwMjRfMDJfMjgvc25ha2VvdXQvaHMxMTFfZGV2ZWxvcGluZ19leWUvc3RhZ2U0CnNvdXJjZSAvZGF0YS8kVVNFUi9jb25kYS9ldGMvcHJvZmlsZS5kL2NvbmRhLnNoICYmIHNvdXJjZSAvZGF0YS8kVVNFUi9jb25kYS9ldGMvcHJvZmlsZS5kL21hbWJhLnNoCm1hbWJhIGFjdGl2YXRlIHJhcGlkc19zaW5nbGVjZWxsCgpweXRob24gfi9naXQvc2NFaWFEX21vZGVsaW5nL3dvcmtmbG93L3NjcmlwdHMvYXBwZW5kX29icy5weSAuLi9oczExMS5hZGF0YS5zb2xvLjIwMjUwMjA0LmRldi5oNWFkIC9ob21lL21jZ2F1Z2hleWQvZ2l0L3NjRWlhRF9tb2RlbGluZy9kYXRhL0h1bWFuX0RldmVsb3BpbmdfRXllX19zdGFnZTRfQ1RjYWxscy5mcmVlemUyMDI1MDIxMS5jc3YuZ3ogIGhzMTExLmFkYXRhLnNvbG8uMjAyNTAyMTEuZGV2LnN0YWdlNENULmg1YWQgLS10cmFuc2Zlcl9jb2x1bW5zIE1ham9yQ2VsbFR5cGUsQ1QKCiMgcnVuIHNjVkkgc25ha2UgcGlwZWxpbmUgYWdhaW4Kc2JhdGNoIC0tdGltZT04OjAwOjAwIHNuYWtlY2FsbC5zaApgYGAKCmBgYHtyfQpvYnNfc3RhZ2U0IDwtIHB1bGxfb2JzKCd+L2RhdGEvc2NFaWFEX21vZGVsaW5nL2hzMTExX2RldmVsb3BpbmdfZXllL3N0YWdlNC9oczExMV9kZXZfZXllX3N0YWdlNF8yMDI1MDIxMV8yMDAwaHZnXzIwMGVfNTBsLm9icy5jc3YuZ3onLCBtYWNoaW5lX2xhYmVsID0gJ3NjYW52aV9DVCcsIGxhYmVsID0gJ0NUJykKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9MTJ9CgpvYnNfc3RhZ2U0JG9icyAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gc2NhbnZpX0NUKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDAuNSkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IC4gJT4lIGdyb3VwX2J5KHNjYW52aV9DVCkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UodW1hcDEgPSBtZWRpYW4odW1hcDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW1hcDIgPSBtZWRpYW4odW1hcDIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHNjYW52aV9DVCwgY29sb3IgPSBzY2FudmlfQ1QpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmdsYXNiZXkoKSkgJT4lIHVubmFtZSgpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGdndGl0bGUoInNjYW52aV9DVCIpCgpvYnNfc3RhZ2U0JG9icyAlPiUgCiAgbGVmdF9qb2luKG9ic19zdGFnZTQkbGFiZWxzLCBieSA9ICdsZWlkZW4zJykgJT4lIAogICNmaWx0ZXIoc2NhbnZpX0NUID09ICdyb2QgKHByZWN1cnNvciknKSAlPiUgCiAgI2ZpbHRlcihsZWlkZW4zICVpbiUgYygyLDYpKSAlPiUgCiAgZ2dwbG90KGFlcyh4PXVtYXAxLHk9dW1hcDIpKSArCiAgc2NhdHRlcm1vcmU6Omdlb21fc2NhdHRlcm1vcmUoYWVzKGNvbG9yID0gYXMuZmFjdG9yKGxlaWRlbjMpKSwgcG9pbnRzaXplID0gMC44LCBhbHBoYSA9IDAuNSkgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhID0gLiAlPiUgZ3JvdXBfYnkobUNULCBsZWlkZW4zKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSh1bWFwMSA9IG1lZGlhbih1bWFwMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bWFwMiA9IG1lZGlhbih1bWFwMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gcGFzdGUwKG1DVCwgJzonLCBsZWlkZW4zKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAnYmxhY2snLCBiZy5jb2xvciA9ICd3aGl0ZScpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjphbHBoYWJldDIoKSwgcGFsczo6Z2xhc2JleSgpLCBwYWxzOjphbHBoYWJldCgpLCBwYWxzOjprZWxseSgpLCBwYWxzOjpicmV3ZXIuc2V0MSgxMCkpICU+JSB1bm5hbWUoKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBnZ3RpdGxlKCJsZWlkZW4zIC0gc2NhbnZpX0NUIikKCm9ic19zdGFnZTQkb2JzICU+JSAKICBnZ3Bsb3QoYWVzKHg9dW1hcDEseT11bWFwMikpICsKICBzY2F0dGVybW9yZTo6Z2VvbV9zY2F0dGVybW9yZShhZXMoY29sb3IgPSBzY2FudmlfQ1QpLCBwb2ludHNpemUgPSAwLjgsIGFscGhhID0gMC41KSArCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZ2d0aXRsZSgic2NhbnZpX0NUIikgKwogIGZhY2V0X3dyYXAofnNjYW52aV9DVCkgKwogIGdndGl0bGUoInNjYW52aV9DVCIpCgoKYGBgCgoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBg